package com.rceeslca.common.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.rceeslca.common.constant.Constant;
import com.rceeslca.common.page.PageData;
import com.rceeslca.common.service.BaseService;
import com.rceeslca.common.utils.ConvertUtils;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * 基础服务类
 * @author rceeslca
 */
public abstract class BaseServiceImpl<M extends BaseMapper<T>,T> implements BaseService<T> {

    @Autowired
    protected M baseDao;

    protected Log log = LogFactory.getLog(getClass());


    /**
     * 获取分页对象
     * @param params 查询参数
     * @param defaultOrderField 默认排序字段
     * @param isAsc 排序方式
     */
    protected IPage<T> getPage(Map<String,Object> params,String defaultOrderField,boolean isAsc){

        //分页参数
        long curPage = 1;
        long limit = 10;

        if(params.get(Constant.PAGE)!=null){
            curPage = Long.parseLong((String)params.get(Constant.PAGE));
        }
        if(params.get(Constant.LIMIT)!=null){
            limit = Long.parseLong((String)params.get(Constant.LIMIT));
        }

        //分页对象
        Page<T> page = new Page<>(curPage,limit);

        //分页参数
        params.put(Constant.PAGE,page);

        //排序字段
        String orderField = (String)params.get(Constant.ORDER_FIELD);
        String order = (String)params.get(Constant.ORDER);

        //前端字段排序
        if(StrUtil.isNotBlank(orderField)&&StrUtil.isNotBlank(order)){
            if(Constant.ASC.equalsIgnoreCase(order)){
                return page.addOrder(OrderItem.asc(orderField));
            }else{
                return page.addOrder(OrderItem.desc(orderField));
            }
        }

        //没有排序字段则不排序
        if(StrUtil.isBlank(defaultOrderField)){
            return page;
        }

        //默认排序
        if(isAsc){
            page.addOrder(OrderItem.asc(defaultOrderField));
        }else{
            page.addOrder(OrderItem.desc(defaultOrderField));
        }

        return page;
    }


    protected <T> PageData<T> getPageData(List<?> list,long total,Class<T> target){
        List<T> targetList = ConvertUtils.sourceToTarget(list,target);

        return new PageData<>(targetList, total);
    }


    protected <T> PageData<T> getPageData(IPage page,Class<T> target){
        return getPageData(page.getRecords(),page.getTotal(),target);
    }


    protected void paramsToLike(Map<String,Object> params,String... likes){
        for(String like : likes){
            String val = (String)params.get(like);
            if(StrUtil.isNotBlank(val)){
                params.put(like,"%"+val+"%");
            }else{
                params.put(like, null);
            }
        }
    }


    /**
     * 判断数据库操作是否成功
     * @param result 数据库操作返回影响条数
     */
    protected static boolean retBool(Integer result){
        return SqlHelper.retBool(result);
    }


    protected Class<M> currentMapperClass(){
        return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(),BaseServiceImpl.class, 0);
    }


    @Override
    public Class<T> currentModelClass(){
        return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(),BaseServiceImpl.class, 1);
    }


    protected String getSqlStatement(SqlMethod sqlMethod){
        return SqlHelper.getSqlStatement(this.currentMapperClass(),sqlMethod);
    }


    @Override
    public boolean insert(T entity){
        return BaseServiceImpl.retBool(baseDao.insert(entity));
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertBatch(Collection<T> entityList){
        return insertBatch(entityList, 100);
    }


    /**
     * 批量插入
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertBatch(Collection<T> entityList,int batchSize){
        String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);
        return executeBatch(entityList,batchSize,(sqlSession, entity)->sqlSession.insert(sqlStatement,entity));
    }


    /**
     * 执行批量操作
     */
    protected <E> boolean executeBatch(Collection<E> list,int batchSize,BiConsumer<SqlSession,E> consumer){
        return SqlHelper.executeBatch(this.currentModelClass(),this.log,list,batchSize,consumer);
    }


    @Override
    public boolean updateById(T entity){
        return BaseServiceImpl.retBool(baseDao.updateById(entity));
    }


    @Override
    public boolean update(T entity,Wrapper<T> updateWrapper){
        return BaseServiceImpl.retBool(baseDao.update(entity,updateWrapper));
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateBatchById(Collection<T> entityList){
        return updateBatchById(entityList,30);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateBatchById(Collection<T> entityList,int batchSize) {
        String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID);
        return executeBatch(entityList,batchSize,(sqlSession,entity)->{
            MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
            param.put(Constants.ENTITY,entity);
            sqlSession.update(sqlStatement,param);
        });
    }


    @Override
    public T selectById(Serializable id){
        return baseDao.selectById(id);
    }


    @Override
    public boolean deleteById(Serializable id){
        return SqlHelper.retBool(baseDao.deleteById(id));
    }


    @Override
    public boolean deleteBatchIds(Collection<? extends Serializable> idList){
        return SqlHelper.retBool(baseDao.deleteBatchIds(idList));
    }

}
